import { Scanner } from "./scanner"
import { isDigit, isString } from "./util"

export enum TokenType {
    STRING,
    NUMBER,
    BOOLEAN,
    COMMA, // ,
    LEFT_BRACE, // {
    RIGHT_BRACE, // }
    LEFT_BRACKET, // [
    RIGHT_BRACKET, // ]
    SEMI_COLON // :
}

export interface Token {
    tokenType: TokenType,
    value: string
}

class Tokenize {
    scanner: Scanner

    private tokens: Array<Token>

    constructor(scanner: Scanner) {
        this.scanner = scanner
        this.tokens = []
    }

    parse() {
        const rules = this.getTokenGetter()

        while (!this.scanner.isAtEnd()) {
            let isMatch = false

            for (let i = 0; i < rules.length; i++) {
                isMatch = rules[i](this)

                if (isMatch) {
                    break
                }
            }

            if (!isMatch) {
                throw new Error('No tokentype matched')
            }
        }
        return this.tokens
    }

    getTokenGetter() {
        const arr:  Array<(tokenize: Tokenize) => boolean> = [
            boolGetter,
            numGetter,
            skipGetter,
            commaGetter,
            leftBraceGetter,
            rightBraceGetter,
            leftBracketGetter,
            rightBracketGetter,
            semicolonGetter,
            stringGetter
        ]

        return arr
    }

    pushToken(token: Token) {
        this.tokens.push(token)
    }

}

function boolGetter(tokenize: Tokenize) {
    const firstChar = tokenize.scanner.peek()

    if (firstChar !== 't' && firstChar !== 'f') {
        return false
    }

    if (firstChar === 't') {
        const isMatch = tokenize.scanner.match("rue")
        if (isMatch) {
            tokenize.pushToken({
                tokenType: TokenType.BOOLEAN,
                value: 'true'
            })
        }

        return isMatch
    }

    if (firstChar === 'f') {
        const isMatch = tokenize.scanner.match("alse")
        if (isMatch) {
            tokenize.pushToken({
                tokenType: TokenType.BOOLEAN,
                value: 'false'
            })
        }

        return isMatch
    }

    return false
}

function numGetter(tokenize: Tokenize) {
    let result = ""
    let isMatch = false

    if (!isDigit(tokenize.scanner.peek())) {
        return isMatch
    }

    while (isDigit(tokenize.scanner.peek())) {
        result += tokenize.scanner.advance()
    }

    isMatch = true

    // 有小数点情况
    if (tokenize.scanner.peek() === "." &&
        isDigit(tokenize.scanner.peekNext())
    ) {
        result += tokenize.scanner.advance()
    } else {
        tokenize.pushToken({
            tokenType: TokenType.NUMBER,
            value: result
        })
        return isMatch
    }

    while (isDigit(tokenize.scanner.peek())) {
        result += tokenize.scanner.advance()
    }

    tokenize.pushToken({
        tokenType: TokenType.NUMBER,
        value: result
    })

    return isMatch
}

function stringGetter(tokenize: Tokenize) {
    // 如果不是双引号开始, 直接结束
    if (tokenize.scanner.peek() !== "\"") {
        return false
    }

    let isMatch = false
    let result = tokenize.scanner.advance()

    while (!tokenize.scanner.isAtEnd() && isString(tokenize.scanner.peek())) {
        const currentChar = tokenize.scanner.peek()

        // 要考虑字符串中有双引号的情况
        if (currentChar === '\\') {
            const quoteMatch = tokenize.scanner.match("\"")
            if (quoteMatch) {
                result += "\\\""
                continue
            }
        }

        result += tokenize.scanner.advance()
    }

    if (tokenize.scanner.isAtEnd()) {
        return isMatch
    }

    if (tokenize.scanner.peek() === "\"") {
        result += tokenize.scanner.advance()
        tokenize.pushToken({
            tokenType: TokenType.STRING,
            value: result
        })
        isMatch = true
    }

    return isMatch
}

function skipGetter(tokenize: Tokenize) {
    const firstChar = tokenize.scanner.peek()

    if (firstChar === " " || 
        firstChar === "\t" ||
        firstChar === "\n"
    ) {
        tokenize.scanner.advance()
        return true
    }

    return false
}

function commaGetter(tokenize: Tokenize) {
    return basicGetter(tokenize, ",", TokenType.COMMA)
}

function leftBraceGetter(tokenize: Tokenize) {
    return basicGetter(tokenize, "{", TokenType.LEFT_BRACE)
}

function rightBraceGetter(tokenize: Tokenize) {
    return basicGetter(tokenize, "}", TokenType.RIGHT_BRACE)
}

function leftBracketGetter(tokenize: Tokenize) {
    return basicGetter(tokenize, "[", TokenType.LEFT_BRACKET)
}

function rightBracketGetter(tokenize: Tokenize) {
    return basicGetter(tokenize, "]", TokenType.RIGHT_BRACKET)
}

function semicolonGetter(tokenize: Tokenize) {
    return basicGetter(tokenize, ":", TokenType.SEMI_COLON)
}

function basicGetter(tokenize: Tokenize, c: string, tokenType: TokenType) {
    if (tokenize.scanner.peek() === c) {
        const value = tokenize.scanner.advance()

        tokenize.pushToken({
            tokenType,
            value
        })
        return true
    } else {
        return false
    }
}

export function tokenize(scanner: Scanner) {
    const tokens = new Tokenize(scanner).parse()
    return tokens
}
